﻿using System;
using System.Collections.Generic;
using log4net;
using Quartz;

namespace CML.Intercamber.Service.TaskManager
{
    /// <summary>
    /// Configs for BaseClusteredTask
    /// </summary>
    public static class BaseSimpleTaskConfiguration
    {
        public static Dictionary<string, IntercambersTasksConfigElement> Configurations = null;
    }

    /// <summary>
    /// Classe pour gérer les locks sur les jobs BaseClusteredTask
    /// </summary>
    public class BaseSimpleTaskLocker
    {
        /// <summary>
        /// Pour gérer un singleton sur l'execute (quartz ne peut pas lancer en parallèle plusieurs instances de ce job)
        /// Contient la liste des instances de BaseClusteredTaskLocker pour chaque tache 
        /// </summary>
        public static readonly Dictionary<string, BaseSimpleTaskLocker> LockerInstances = new Dictionary<string, BaseSimpleTaskLocker>();

        public readonly object ObjectLocker = new Object();
        public bool IsRunning;
        public DateTime? LastRunSuccessDate;

        /// <summary>
        /// Pour un taskName donné, on récupère la date de dernier run en succès (meme si ce dernier run n'a rien traité)
        /// </summary>
        /// <param name="className"></param>
        /// <returns></returns>
        public static DateTime? TaskLastSuccessTime(string className)
        {
            if (LockerInstances.ContainsKey(className))
                return LockerInstances[className].LastRunSuccessDate;
            return null;
        }
    }

    /// <summary>
    /// Ce template de job quartz permet de définir une tache simple : la fonction JobTraitement est appellée en single instance
    /// </summary>
    public abstract class BaseSimpleTask : IJob
    {

        #region variables globales

        /// <summary>
        /// cache pour le nom de la classe de l'instance
        /// </summary>
        private string currentTypeName;

        protected ILog Logger;

        #endregion

        #region constructeur

        protected BaseSimpleTask()
        {
            Type myType = GetType();
            Logger = LogManager.GetLogger(myType);
        }
        #endregion

        #region Gestion de la concurence
        /// <summary>
        /// Obtient le locker associé à ce job
        /// </summary>
        private BaseSimpleTaskLocker GetLocker
        {
            get
            {
                string className = GetType().ToString();
                if (!BaseSimpleTaskLocker.LockerInstances.ContainsKey(className))
                    BaseSimpleTaskLocker.LockerInstances.Add(className, new BaseSimpleTaskLocker());
                return BaseSimpleTaskLocker.LockerInstances[className];
            }
        }
        #endregion

        #region methode Execute de Quartz

        /// <summary>
        /// utilise SmartThreadPool (http://www.codeproject.com/Articles/7933/Smart-Thread-Pool) 
        /// </summary>
        public void Execute(IJobExecutionContext context)
        {
            // On ne permet qu'une seule execution parallèle du job quartz. 
            // Si le run précédent n'est pas terminé, on ne fait rien, et c'est le suivant qui sera (peut etre) executé
            BaseSimpleTaskLocker locker = GetLocker;
            if (!locker.IsRunning)
                lock (locker.ObjectLocker)
                {
                    locker.IsRunning = true;
                    currentTypeName = GetType().Name;
                    Logger.Info(FormatTaskLogMessage("Demarrage du job"));

                    bool success = JobTraitement();

                    Logger.Info(FormatTaskLogMessage("Fin du job"));
                    if (success)
                        locker.LastRunSuccessDate = DateTime.Now;
                    locker.IsRunning = false;
                }
        }
        #endregion

        #region A utiliser pour les logs !

        /// <summary>
        /// A utiliser pour les logs dans les classes qui héritent !!
        /// </summary>
        protected string FormatTaskLogMessage(string message)
        {
            return String.Format("[{0}] {1}", currentTypeName, message);
        }

        #endregion

        #region fonction à implémenter

        /// <summary>
        /// A implémenter : fonction executée à chaque occurence de cette tache
        /// </summary>
        protected abstract bool JobTraitement();

        #endregion

    }
}
